package org.oslims.client.presenter;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.oslims.client.AsyncCall;
import org.oslims.client.MainEventBus;
import org.oslims.client.service.DepartmentServiceAsync;
import org.oslims.client.service.UserServiceAsync;
import org.oslims.client.uicomponents.SortableHeader;
import org.oslims.client.uicomponents.editbox.EditOperatorBox;
import org.oslims.client.view.impl.OperatorEditorViewImpl;
import org.oslims.domain.Department;
import org.oslims.domain.Operator;

import com.google.gwt.cell.client.ButtonCell;
import com.google.gwt.cell.client.CheckboxCell;
import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.TextColumn;
import com.google.gwt.user.client.ui.TabPanel;
import com.google.gwt.validation.client.InvalidConstraint;
import com.google.gwt.validation.client.interfaces.IValidator;
import com.google.gwt.view.client.ListView;
import com.google.gwt.view.client.Range;
import com.google.gwt.view.client.ListView.Delegate;
import com.mvp4g.client.annotation.InjectService;
import com.mvp4g.client.annotation.Presenter;
import com.mvp4g.client.presenter.BasePresenter;

@Presenter(view = OperatorEditorViewImpl.class)
public class OperatorPresenter extends BasePresenter<OperatorEditorViewImpl, MainEventBus> {

	private UserServiceAsync userService;
	private DepartmentServiceAsync departmentService;

	private IValidator<Operator> operatorValidator;

	private Map<Long, Department> departments;

	private String currentSortProperty = "firstName";
	private Boolean currentSortDesc = false;

	public OperatorPresenter() {
		operatorValidator = GWT.create(Operator.class);
		departments = new HashMap<Long, Department>();
	}

	public void onInizializeOperatorManagement(TabPanel tp) {
		tp.add(view.getViewWidget(), "Operator");

		tp.addSelectionHandler(new SelectionHandler<Integer>() {

			@Override
			public void onSelection(SelectionEvent<Integer> event) {
				if (event.getSelectedItem() == 3) {
					refreshDepartments();
					refreshData();
				}

			}
		});
	}

	@Override
	public void bind() {
		// refreshOperators();

		bindEditBox();
		addColumns();
		setDelegate();
	}

	private void refreshData() {
		userService.getOperatorCount(new AsyncCall<Integer>() {

			@Override
			protected void onFailureE(Throwable caught) {
				eventBus.error(caught.getMessage());
			}

			@Override
			protected void onSuccessE(Integer result) {
				view.getTable().setDataSize(result, true);

				if (result < view.getDefaultPageSize()) {
					view.getPager().setVisible(false);
					view.getTable().setPageSize(result);
				}
				view.getTable().refresh();
			}
		});
	}

	private void setDelegate() {
		view.getTable().setDelegate(new Delegate<Operator>() {

			@Override
			public void onRangeChanged(ListView<Operator> listView) {

				final Range r = listView.getRange();

				userService.getAllOperators(r.getStart(), r.getLength(), currentSortProperty, currentSortDesc, new AsyncCall<List<Operator>>() {

					@Override
					protected void onFailureE(Throwable caught) {
						eventBus.error(caught.getMessage());
					}

					@Override
					protected void onSuccessE(List<Operator> result) {
						view.getTable().setData(r.getStart(), r.getLength(), result);
						view.getTable().redraw();
					}
				});
			}
		});
	}

	private void addColumns() {
		final SortableHeader nameHeader = new SortableHeader("First Name", "firstName");
		nameHeader.setSorted(true);

		nameHeader.setUpdater(new ValueUpdater<String>() {

			@Override
			public void update(String value) {
				nameHeader.toggleReverseSort();
				currentSortProperty = nameHeader.getPropertyName();
				currentSortDesc = nameHeader.getReverseSort();
				view.getTable().refresh();
				view.getTable().refreshHeaders();
			}
		});

		view.getTable().addColumn(new TextColumn<Operator>() {

			@Override
			public String getValue(Operator object) {
				return object.getFirstName();
			}
		}, nameHeader);

		view.getTable().addColumn(new TextColumn<Operator>() {

			@Override
			public String getValue(Operator object) {
				return object.getLastName();
			}
		}, "Last Name");

		view.getTable().addColumn(new TextColumn<Operator>() {

			@Override
			public String getValue(Operator object) {
				return object.getEMail();
			}
		}, "EMail");

		view.getTable().addColumn(new Column<Operator, Boolean>(new CheckboxCell()) {

			@Override
			public Boolean getValue(Operator object) {
				return object.getActive();
			}
		}, "Active");

		view.getTable().addColumn(new TextColumn<Operator>() {

			@Override
			public String getValue(Operator object) {

				String deps = "";

				for (Department d : object.getDepartments()) {
					deps += d.getName() + ", ";
				}

				if (!deps.isEmpty()) {
					return deps.substring(0, deps.length() - 2);
				}

				return "";

			}
		}, "Departments");

		Column<Operator, String> editColumn = new Column<Operator, String>(new ButtonCell()) {

			@Override
			public String getValue(Operator object) {
				return "Edit";
			}
		};

		editColumn.setFieldUpdater(new FieldUpdater<Operator, String>() {

			@Override
			public void update(int index, Operator object, String value) {
				List<Department> selectedDepartments = object.getDepartments();

				List<Department> unselectedDepartments = new ArrayList<Department>();

				for (Department d : departments.values()) {
					if (!selectedDepartments.contains(d)) {
						unselectedDepartments.add(d);
					}
				}
				view.getEditOperatorBox().getDepartmentSelection().setItems(unselectedDepartments, selectedDepartments);
				view.getEditOperatorBox().showEdit(object);
			}
		});

		view.getTable().addColumn(editColumn);
	}

	private void bindEditBox() {
		view.getAddOperatorButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				view.getEditOperatorBox().getDepartmentSelection().setItems(departments.values(), new ArrayList<Department>());
				view.getEditOperatorBox().showNew();
			}
		});

		view.getEditOperatorBox().getCancelButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				view.getEditOperatorBox().hide();
			}
		});

		view.getEditOperatorBox().getSaveButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				if (view.confirmEdit("Do you want to save your changes?"))
					saveoperator();
			}

		});
	}

	@InjectService
	public void setDepartmentService(DepartmentServiceAsync departmentService) {
		this.departmentService = departmentService;
	}

	@InjectService
	public void setUserService(UserServiceAsync userService) {
		this.userService = userService;
	}

	private void saveoperator() {

		EditOperatorBox editBox = view.getEditOperatorBox();
		Operator tempOperator = editBox.getOperator();

		if (tempOperator == null)
			tempOperator = new Operator();

		tempOperator.setActive(editBox.getActive().getValue());
		tempOperator.setEMail(editBox.getEMail().getValue());
		tempOperator.setFirstName(editBox.getFirstName().getValue());
		tempOperator.setLastName(editBox.getLastName().getValue());
		tempOperator.setPassword(editBox.getPassword().getValue());

		tempOperator.getDepartments().clear();

		for (Department d : view.getEditOperatorBox().getDepartmentSelection().getSelectedItems()) {
			tempOperator.getDepartments().add(d);
		}

		Set<InvalidConstraint<Operator>> operatorErrors = operatorValidator.validate(tempOperator);

		if (operatorErrors.size() > 0) {
			for (InvalidConstraint<Operator> ic : operatorErrors) {
				editBox.showError(ic.getItemName(), ic.getMessage());
			}
			return;
		}

		userService.saveOrUpdateOperator(tempOperator, new AsyncCall<Void>() {

			@Override
			public void onSuccessE(Void result) {
				view.getEditOperatorBox().hide();
				refreshData();
			}

			@Override
			public void onFailureE(Throwable error) {
				eventBus.error(error.getMessage());
			}
		});
	}

	private void refreshDepartments() {

		departmentService.getAllDepartments(new AsyncCall<List<Department>>() {

			@Override
			public void onSuccessE(List<Department> result) {

				departments.clear();

				for (Department d : result) {
					departments.put(d.getId(), d);
				}
			}

			@Override
			public void onFailureE(Throwable error) {
				eventBus.error(error.getMessage());
			}
		});
	}

}
